import Constant from "../common/baseElem"

abstract class MatchStrategy {
    protected isReverse: boolean
    constructor(isReverse: boolean = false) {
        this.isReverse = isReverse
    }
    abstract isMatch(s: string, edge?: string): boolean
}

export class EpsilonMatchStrategy extends MatchStrategy {
    public isMatch(s: string, edge: string): boolean {
        return true
    }
}

export class CharMatchStrategy extends MatchStrategy {
    
    public isMatch(s: string, edge: string): boolean {
        return s === edge
    } 
}

export class CharSetMatchStrategy extends MatchStrategy {
    public isMatch(s: string, charSet: string): boolean {
        let result = false,
            reverse = false

        for(let i = 0; i < charSet.length; i++) {
            if(charSet.charAt(0) === '^') {
                reverse = true
                continue
            }

            if(charSet.charAt(i) === '-') {
                result ||= (s >= charSet.charAt(i-1) && s <= charSet.charAt(i+1))
            }

            if(s === charSet.charAt(i)) {
                result = true
                break
            }
        }

        

        if(reverse) return !result
        else return result

    }
}

export class DigitalMatchStrategy extends MatchStrategy {
    public isMatch(s: string, edge: string): boolean {
        let result = s >= '0' && s <= '9'
        return this.isReverse ? !result : result
    }
}

export class WordMatchStrategy extends MatchStrategy {
    public isMatch(s: string, edge: string): boolean {
        let result  =   s >= 'A' && s <= 'Z' ||
                        s >= 'a' && s <= 'z' ||
                        s >= '0' && s <= '9'
        
        return this.isReverse ? !result : result
    }
}

export class SpaceMatchStrategy extends MatchStrategy {
    public isMatch(s: string, edge: string): boolean {
        let result =  (s === '\v' || s === '\f' || s === '\n' || s === '\r' || s === '\t' || s == ' ')
        return this.isReverse ? !result : result
    }
}

export class DotMatchStrategy extends MatchStrategy {
    public isMatch(s: string, edge: string): boolean {
        return s !== '\n' && s !== '\r'
    }
}


export class MatchStrategyMap {

    private static map: Map<string, MatchStrategy> = new Map()

    public static setStrategy(obj: {[key: string]: MatchStrategy}) {
        for(let s in obj) {
            this.map.set(s, obj[s])
        }
    }

    public static getStrategy(s: string) {
        if(this.map.has(s)) {
            return this.map.get(s)
        }

        if(s.length === 1) {
            return this.map.get(Constant.CHAR)
        }
        else {
            return this.map.get(Constant.CHARSET)
        }
    }
}

MatchStrategyMap.setStrategy({
    [Constant.EPSILON]: new EpsilonMatchStrategy(),
    [Constant.CHAR]: new CharMatchStrategy(),
    [Constant.CHARSET]: new CharSetMatchStrategy(),
    '\\d': new DigitalMatchStrategy(false),
    '\\D': new DigitalMatchStrategy(true),
    '\\w': new WordMatchStrategy(false),
    '\\W': new WordMatchStrategy(true),
    '\\s': new SpaceMatchStrategy(false),
    '\\S': new SpaceMatchStrategy(true),
    '.': new DotMatchStrategy()
})